#!/usr/bin/env python3  /*设定脚本文件直接能执行*/
# -*- coding:utf-8 -*-   /*设置编码*/
"""
@Author: zhuo
@Software: PyCharm
@File: tQMainForm.py
@Time: 2023/12/28 16:31
@Function：无边框主窗体框架：可实现主题切换、增加导航栏(2种方式)、窗体大小缩放等功能
"""

import sys
from datetime import datetime
from tUI.tQControlForm import ctQControlForm
from tUI.tQParaSetForm import ctQParaSetForm
from tUI.tQTitleBar import *
from tUI.tQVerticalTabWidget import *
from tUI.tQWorkbenchForm import ctQWorkbenchForm
from tUI.tQSerialForm import ctQSerialForm, GLOBAL_SRL_SIGNAL
from PySide6.QtCore import Qt, QPoint, QTimer
from PySide6.QtWidgets import QWidget, QPushButton, QHBoxLayout, QLabel, QMessageBox, QVBoxLayout


class ctQMainWindow(QMainWindow):
    # 初始化
    def __init__(self, title, icon):
        """
        function:  初始化
              in:  title：窗体标题
                   icon：窗体图标路径
                   widthRatio：宽度比例(0~1)
                   heightRatio：高度比例(0~1)
             out:  None
          return:  None
          others:  Initialize
        """
        # 继承自父类
        super(ctQMainWindow, self).__init__()

        # <editor-fold desc="创建并设置窗体">'

        # 隐藏主窗口边界
        self.setWindowFlags(Qt.FramelessWindowHint)
        # 无边框窗体实现点击任务栏图标最小化，再次点击显示窗口
        self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint | Qt.WindowSystemMenuHint |
                            Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint)
        # central Widget
        self.widget = QWidget(self)
        # 主窗口设置窗口中心的控件
        self.setCentralWidget(self.widget)
        # 创建标题栏
        self.title = ctQTitleBar(self)
        # 创建标题栏标题
        self.title.f_setTitle(title)
        # 设置标题栏图标
        self.title.f_setIcon(QPixmap(icon))

        # # 设置窗体大小并移动到屏幕中间位置
        # self.__f_setAndMoveFormFunc(widthRatio, heightRatio)
        # 调用鼠标跟踪标识及无边框缩放功能初始化函数
        self.__f_initDrag()

        # 主Layout
        self.mainLayout = QVBoxLayout(self.widget)
        # 将标题栏添加到主布局中
        self.mainLayout.addWidget(self.title)
        # 初始化状态栏
        self.__f_initStatusBar()

        # </editor-fold>

        # <editor-fold desc="创建tab">
        # icon列表
        lstIcon = ["./ico/workbench.png", "./ico/connect.png", "./ico/control.png", "./ico/para.png"]
        # 标签列表
        lblLst = ["工作台", "通讯连接", "电机控制", "参数设置"]

        # 一、通过QPushButton实现的选项卡函数
        self.btnDic, self.tabDic = self.__f_createBtnTabFunc(4, lstIcon, lblLst)
        # 二、通过TanWidget创建选项卡函数
        # self.tabDic = self.__f_createTabFunc(3, lstIcon, lblLst)
        # </editor-fold>

        # 串口状态改变信号
        GLOBAL_SRL_SIGNAL.srlBtnClicked.connect(self.__f_srlStateChangedUpdateUI)

        # <editor-fold desc="创建Tab页界面">
        # 创建串口界面 1
        self.serialTab = ctQSerialForm(self.tabDic[1]["widget"])
        # 通讯类实例化
        self.rtu = self.serialTab.rtu
        # 创建工作台界面 0
        dicParaMsg = {"相电流": {"unit": "A", "txt": None},
                      "母线电压": {"unit": "V", "txt": None},
                      "转速": {"unit": "rpm", "txt": None},
                      "温度": {"unit": "℃", "txt": None},
                      "运行状态": {"txt": None}
                      }
        self.workbenchTab = ctQWorkbenchForm(self.tabDic[0]["widget"], dicParaMsg)
        # 创建控制界面 2
        self.controlTab = ctQControlForm(self.tabDic[2]["widget"], self.rtu, self.workbenchTab)
        # 创建参数设置界面 3
        self.paraSetTab = ctQParaSetForm(self.tabDic[3]["widget"], self.rtu, self.controlTab, self.workbenchTab)
        # </editor-fold>

    # <editor-fold desc="主窗体显示用到的函数">
    # 设置窗体大小并移动到屏幕中间位置
    def __f_setAndMoveFormFunc(self, widthRatio, heightRatio):
        """
        function:  移动窗体到屏幕中间位置
              in:  widthRatio：宽度比例
                   heightRatio：高度比例
             out:  None
          return:  None
          others:  Set Form Size And Move To The Middle Of The Screen
        """
        # 获取缩放比率
        self.__m_ratio = QGuiApplication.primaryScreen().devicePixelRatio()
        # 获取屏幕的宽
        self.__m_width = QGuiApplication.primaryScreen().availableGeometry().width()
        # 获取屏幕的高
        self.__m_height = QGuiApplication.primaryScreen().availableGeometry().height()
        # 0.645
        self.__m_widthRatio = widthRatio
        # 0.72
        self.__m_heightRatio = heightRatio
        # 设置窗体大小
        self.setGeometry(0, 0, self.__m_widthRatio * self.__m_width, self.__m_heightRatio * self.__m_height)
        # 获得窗口的数据
        q_rect = self.frameGeometry()
        # 获得可视窗口的中心点坐标
        center_point = QGuiApplication.primaryScreen().geometry().center()
        # 将QRect移动到中心点
        q_rect.moveCenter(center_point)
        # 将窗口本身移动到中心点
        self.move(q_rect.topLeft())

    # 窗体主题改变后更改UI函数
    def __f_themeChangedFunc(self, theme):
        """
        function:  窗体主题改变后更改UI函数
              in:  theme：主题索引 0：dark；else：light
             out:  None
          return:  None
          others:  Change The UI Func After The Form Theme Changed
        """
        # 更改GraphicsLayoutWidget的背景颜色
        if theme == 0:
            # 背景色改为dark
            self.pw.setBackground(COLOR_DARK)
        else:
            # 背景色改为亮色
            self.pw.setBackground(COLOR_LIGHT)

    # 重写关闭事件
    def closeEvent(self, event):
        """
        function:  重写关闭事件
              in:  event：closeEvent
             out:  None
          return:  None
          others:  Override Close Event
        """
        # 弹出关闭提示框
        reply = QMessageBox.question(self, '关闭提示', "是否要退出界面？",
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        # 如果用户选择Yes
        if reply == QMessageBox.Yes:
            # 执行操作
            event.accept()
        # 如果用户选择No
        elif reply == QMessageBox.No:
            # 关闭提示窗口
            event.ignore()

    # </editor-fold>

    # <editor-fold desc="创建选项卡的两种方式">
    # 创建选项卡函数
    def __f_createTabFunc(self, tabNum, iconLst, lblLst):
        """
        function:  创建选项卡函数
              in:  tabNum：创建选项卡的数量；
                   iconLst：选项卡图标列表；
                   lblLst：选项卡标签列表；
             out:  tabDic：选项卡字典
          return:  dic
          others:  Create TabWidget Func
        """
        # 创建QTabWidget()
        self.tab = ctQVerticalTabWidget()
        tabDic = {}
        for i in range(tabNum):
            widget = QWidget()
            # 设置objectName
            widget.setObjectName(f"tab{i}")
            tabDic[i] = {"widget": widget, "objectName": widget.objectName(), "icon": iconLst[i], "label": lblLst[i]}
        # TabWidget添加选项卡
        for i in range(tabNum):
            self.tab.addTab(tabDic[i]["widget"], QPixmap(tabDic[i]["icon"]), tabDic[i]["label"])
        # 主界面中添加Tab控件
        self.mainLayout.addWidget(self.tab)
        return tabDic

    # 基于QPushButton实现的选项卡函数
    def __f_createBtnTabFunc(self, btnNum, iconLst, lblLst):
        """
        function:  基于QPushButton实现的选项卡函数
              in:  btnNum：创建选项卡的数量；
                   iconLst：选项卡图标列表；
                   lblLst：选项卡标签列表；
             out:  btnDic：按键字典；
                   tabDic：选项卡字典；
          return:  dic
          others:  Create Button Tab Func
        """
        # 创建布局
        self.underLayout = QHBoxLayout()
        self.btnLayout = QVBoxLayout()
        self.tabLayout = QHBoxLayout()
        # 创建TabWidget
        self.tab = QTabWidget()
        self.tabLayout.addWidget(self.tab)
        # 初始化button字典
        btnDic = {}
        # 初始化label字典
        lblDic = {}
        tabDic = {}
        # Tab控件按键点击信号关联槽函数
        btnClickSignal.themeBtnClicked.connect(self.__f_themeChangedUpdateUI)
        # 循环创建Button、Label、选项卡
        for i in range(btnNum):
            btn = QPushButton()
            # button 设置objectName
            btn.setObjectName(f"btn{i}")
            # button 设置大小
            btn.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
            # button 设置固定大小
            btn.setFixedSize(50, 50)
            # button 设置Icon
            btn.setStyleSheet("#btn%s{border-image:url(%s);background-color:%s; }" % (i, iconLst[i], COLOR_DARK))
            # button 字典中添加内容
            btnDic[i] = {"btn": btn, "objectName": btn.objectName(), "icon": iconLst[i], "label": lblLst[i]}
            # 创建标签
            lbl = QLabel()
            # 标签设置固定尺寸
            lbl.setFixedSize(60, 20)
            # 设置标签文本
            lbl.setText(lblLst[i])
            lblDic[i] = lbl
            # 选项卡添加Widget
            widget = QWidget()
            # 设置objectName
            widget.setObjectName(f"tab{i}")
            # tab字典中添加值
            tabDic[i] = {"widget": widget, "objectName": widget.objectName(),
                         "icon": iconLst[i], "label": lblLst[i], "index": i}
        # 布局中添加Button、Label
        for i in range(btnNum):
            self.btnLayout.addWidget(btnDic[i]["btn"])
            self.btnLayout.addWidget(lblDic[i])
        # 创建一个弹簧，设置QSizePolicy策略
        spacerBtn = QSpacerItem(50, 600, QSizePolicy.Preferred, QSizePolicy.Expanding)
        # TabWidget添加选项卡
        for i in range(btnNum):
            self.tab.addTab(tabDic[i]["widget"], QPixmap(tabDic[i]["icon"]), tabDic[i]["label"])
            # i=i, 前一个i表示i是lambda函数的一个参数，后一个i向i参数传入默认值
            btnDic[i]["btn"].clicked.connect(lambda x=i, i=i: self.tab.setCurrentIndex(tabDic[x]["index"]))
        # 布局中添加弹簧，弹簧的大小可以根据实际修改
        self.btnLayout.addItem(spacerBtn)
        # 添加布局
        self.underLayout.addLayout(self.btnLayout)
        self.underLayout.addLayout(self.tabLayout)
        self.mainLayout.addLayout(self.underLayout)
        # 隐藏QTabWidget的标签名
        self.tab.tabBar().hide()
        # 返回值
        return btnDic, tabDic

    # </editor-fold>

    # <editor-fold desc="无边框窗体缩放功能">
    # 鼠标跟踪标识及无边框缩放功能初始化
    def __f_initDrag(self):
        """
        function:  鼠标跟踪标识及无边框缩放功能初始化
              in:  None
             out:  None
          return:  None
          others:  Mouse Trace & Borderless Scaling Function Identifier Initialization
        """
        # 设置widget鼠标跟踪
        self.setMouseTracking(True)
        # 设置边界宽度为5
        self.__m_padding = 5
        # 鼠标跟踪标识初始化
        self.__m_moveDrag = False
        self.__m_cornerDrag = False
        self.__m_bottomDrag = False
        self.__m_rightDrag = False

    # 重写窗口调整大小事件
    def resizeEvent(self, event):
        """
        function:  重写窗口调整大小事件
              in:  event: QResizeEvent
             out:  None
          return:  None
          others:  Override Window Resize Event
        """
        # 调整边界范围实现鼠标缩放窗口大小
        self.__m_rightRect = [QPoint(x, y) for x in range(self.width() - self.__m_padding, self.width() + 1)
                              for y in range(1, self.height() - self.__m_padding)]
        self.__m_bottomRect = [QPoint(x, y) for x in range(1, self.width() - self.__m_padding)
                               for y in range(self.height() - self.__m_padding, self.height() + 1)]
        self.__m_cornerRect = [QPoint(x, y) for x in range(self.width() - self.__m_padding, self.width() + 1)
                               for y in range(self.height() - self.__m_padding, self.height() + 1)]

    # 重写鼠标点击事件
    def mousePressEvent(self, event):
        """
        function:  重写鼠标点击事件
              in:  event：mousePressEvent
             out:  None
          return:  None
          others:  Override Mouse Press Event
        """
        # 鼠标左键点击右下角边界区域
        if (event.button() == Qt.LeftButton) and (event.position() in self.__m_cornerRect):
            # 鼠标右下角跟踪标识置为True
            self.__m_cornerDrag = True
            event.accept()
        # 鼠标左键点击右侧边界区域
        elif (event.button() == Qt.LeftButton) and (event.position() in self.__m_rightRect):
            # 鼠标右下角跟踪标识置为True
            self.__m_rightDrag = True
            event.accept()
        # 鼠标左键点击下侧边界区域
        elif (event.button() == Qt.LeftButton) and (event.position() in self.__m_bottomRect):
            # 鼠标右下角跟踪标识置为True
            self.__m_bottomDrag = True
            event.accept()

    # 重写鼠标移动事件
    def mouseMoveEvent(self, event):
        """
        function:  重写鼠标释放事件
              in:  event：mouseMoveEvent
             out:  None
          return:  None
          others:  Override Mouse Move Event
        """
        # 判断鼠标位置切换鼠标手势，如果鼠标位置在右下角、下侧、右侧，切换鼠标手势
        if event.position() in self.__m_cornerRect:
            self.setCursor(Qt.SizeFDiagCursor)
        elif event.position() in self.__m_bottomRect:
            self.setCursor(Qt.SizeVerCursor)
        elif event.position() in self.__m_rightRect:
            self.setCursor(Qt.SizeHorCursor)
        # 如果窗口正在缩放，鼠标手势不做变化
        elif self.__m_rightDrag:
            self.setCursor(Qt.SizeHorCursor)
        elif self.__m_bottomDrag:
            self.setCursor(Qt.SizeVerCursor)
        elif self.__m_cornerDrag:
            self.setCursor(Qt.SizeFDiagCursor)
        # 其他情况，鼠标手势复原
        else:
            self.setCursor(Qt.ArrowCursor)

        # 当鼠标左键点击不放及满足点击区域的要求后，分别实现不同的窗口调整
        if Qt.LeftButton and self.__m_rightDrag:
            # 右侧调整窗口宽度
            self.resize(event.position().x(), self.height())
            event.accept()
        elif Qt.LeftButton and self.__m_bottomDrag:
            # 下侧调整窗口高度
            self.resize(self.width(), event.position().y())
            event.accept()
        elif Qt.LeftButton and self.__m_cornerDrag:
            # 右下角同时调整高度和宽度
            self.resize(event.position().x(), event.position().y())
            event.accept()

    # 重写鼠标释放事件
    def mouseReleaseEvent(self, event):
        """
        function:  重写鼠标释放事件
              in:  event：mouseReleaseEvent
             out:  None
          return:  None
          others:  Override Mouse Release Event
        """
        # 鼠标释放后，鼠标跟踪标识复位
        self.__m_moveDrag = False
        self.__m_cornerDrag = False
        self.__m_bottomDrag = False
        self.__m_rightDrag = False

    # </editor-fold>

    # <editor-fold desc="状态栏">
    # 初始化状态栏
    def __f_initStatusBar(self):
        """
        function:  初始化状态栏
              in:  None
             out:  None
          return:  None
          others:  Initializes Status Bar
        """
        # 添加一个显示永久信息的标签控件
        self.info = QLabel(self)
        # 状态栏设置时间信息
        self.info.setText(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
        # 创建定时器
        self.timer = QTimer()
        self.timer.timeout.connect(self.__f_statusBarRefresh)
        # 定时器开启
        self.timer.start(1000)
        # 创建状态栏
        self.statusBar = QStatusBar()
        # 设置状态栏的ObjectName
        self.statusBar.setObjectName("statusBar")
        # 设置在右下角是否有三角形
        self.statusBar.setSizeGripEnabled(False)
        # 设置到状态栏的最右边
        self.info.setAlignment(Qt.AlignRight)
        # 将标签控件添加到状态栏中
        self.statusBar.addPermanentWidget(self.info)
        self.setStatusBar(self.statusBar)
        # 设置状态栏的背景色
        self.statusBar.setStyleSheet("#statusBar{background-color:%s; }" % COLOR_DARK)

    # 状态栏时间刷新函数
    def __f_statusBarRefresh(self):
        """
        function:  状态栏时间刷新函数
              in:  None
             out:  None
          return:  None
          others:  Status Bar Time Refresh Func
        """
        # 设置时间输出格式
        new_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        self.info.setText(new_time)

    # </editor-fold>

    # <editor-fold desc="接收信号后更新UI界面">
    # 串口状态改变更新UI界面
    def __f_srlStateChangedUpdateUI(self, state):
        """
        function:  串口状态改变更新UI界面
              in:  state：串口状态字符串
             out:  None
          return:  None
          others:  The Status Of The Serial Port Changed Update The UI
        """
        # 更新状态栏展示信息
        self.statusBar.showMessage(state)

    # GUI主题切换后更改UI界面
    def __f_themeChangedUpdateUI(self, theme):
        """
        function:  GUI主题切换后更改UI界面
              in:  theme：主题；0：dark；1：light
             out:  None
          return:  None
          others:  Change UI After Switching The GUI Theme
        """
        length = len(self.btnDic)
        if theme == 0:
            # 设置状态栏的背景色
            self.statusBar.setStyleSheet("#statusBar{background-color:%s; }" % COLOR_DARK)
            self.controlTab.pw.setBackground(COLOR_DARK)
            # 重新设置按键的背景色
            for i in range(length):
                self.btnDic[i]["btn"].setStyleSheet(
                    "#btn%s{border-image:url(%s);background-color:%s; }" % (i, self.btnDic[i]["icon"], COLOR_DARK))
        else:
            # 设置状态栏的背景色
            self.statusBar.setStyleSheet("#statusBar{background-color:%s; }" % COLOR_LIGHT)
            self.controlTab.pw.setBackground(COLOR_LIGHT)
            # 重新设置按键的背景色
            for i in range(length):
                self.btnDic[i]["btn"].setStyleSheet(
                    "#btn%s{border-image:url(%s);background-color:%s; }" % (i, self.btnDic[i]["icon"], COLOR_LIGHT))
    # </editor-fold>


def showUI():
    # 分辨率适配问题，解决界面模糊，缩放比例问题
    QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    # 任何一个图形化的qt程序必须要创建QApplication的实例。
    app = QApplication(sys.argv)

    screen = app.primaryScreen()
    # 获取屏幕的DPI
    dpi_x = screen.physicalDotsPerInchX()
    dpi_y = screen.physicalDotsPerInchY()
    # 定义高DPI的阈值，例如 100 DPI
    HIGH_DPI_THRESHOLD = 100

    # AA_EnableHighDpiScaling
    if dpi_x > HIGH_DPI_THRESHOLD or dpi_y > HIGH_DPI_THRESHOLD:
        # 适应高DPI设备
        QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
        # 解决图片在不同分辨率显示模糊问题
        QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
    else:
        # 适应低DPI设备
        QApplication.setAttribute(Qt.AA_DisableHighDpiScaling)

    # 加载 icon 注意文件路径问题，否则会导致加载失败
    icon = "./ico/haier.png"
    app.setWindowIcon(QIcon(icon))

    # 类的实例化  0.84, 0.88
    win = ctQMainWindow("变频器测控系统(VFDTS)", icon)
    win.show()
    # 设置窗体的最大化
    win.showMaximized()
    # 调用QApplication的exec__函数，不然程序会一闪而过
    sys.exit(app.exec_())


if __name__ == '__main__':
    app = QApplication([])
    icon = "../ico/motor.png"
    app.setWindowIcon(QIcon(icon))
    win = ctQMainWindow("工业风机上位机控制系统", icon, 0.645, 0.72)
    win.show()
    app.exec()
